home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 11 / Cream of the Crop 11-1.iso / math / ast51src.zip / GENERAL.C < prev    next >
C/C++ Source or Header  |  1995-12-31  |  29KB  |  1,082 lines

  1. /*
  2. ** Astrolog (Version 5.10) File: general.c
  3. **
  4. ** IMPORTANT NOTICE: The graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1995 by Walter D. Pullen
  6. ** (Astara@msn.com). Permission is granted to freely use and
  7. ** distribute these routines provided one doesn't sell, restrict, or
  8. ** profit from them in any way. Modification is allowed provided these
  9. ** notices remain with any altered or edited versions of the program.
  10. **
  11. ** The main planetary calculation routines used in this program have
  12. ** been Copyrighted and the core of this program is basically a
  13. ** conversion to C of the routines created by James Neely as listed in
  14. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  15. ** available from Matrix Software. The copyright gives us permission to
  16. ** use the routines for personal use but not to sell them or profit from
  17. ** them in any way.
  18. **
  19. ** The PostScript code within the core graphics routines are programmed
  20. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  21. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  22. **
  23. ** The extended accurate ephemeris databases and formulas are from the
  24. ** calculation routines in the program "Placalc" and are programmed and
  25. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  26. ** (alois@azur.ch). The use of that source code is subject to
  27. ** regulations made by Astrodienst Zurich, and the code is not in the
  28. ** public domain. This copyright notice must not be changed or removed
  29. ** by any user of this program.
  30. **
  31. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  32. ** X Window graphics initially programmed 10/23-29/1991.
  33. ** PostScript graphics initially programmed 11/29-30/1992.
  34. ** Last code change made 12/27/1995.
  35. */
  36.  
  37. #include "astrolog.h"
  38.  
  39.  
  40. /*
  41. ******************************************************************************
  42. ** General Procedures.
  43. ******************************************************************************
  44. */
  45.  
  46. /* Swap two floating point values. */
  47.  
  48. void SwapR(d1, d2)
  49. real *d1, *d2;
  50. {
  51.   real temp;
  52.  
  53.   temp = *d1; *d1 = *d2; *d2 = temp;
  54. }
  55.  
  56.  
  57. /* Return the length of a string (not counting the null terminator). */
  58.  
  59. int CchSz(sz)
  60. CONST char *sz;
  61. {
  62.   int i;
  63.  
  64.   for (i = 0; *sz++; i++)
  65.     ;
  66.   return i;
  67. }
  68.  
  69.  
  70. /* Compare two strings. Return 0 if they are equal, a positive value if  */
  71. /* the first string is greater, and a negative if the second is greater. */
  72.  
  73. int NCompareSz(s1, s2)
  74. CONST char *s1, *s2;
  75. {
  76.   while (*s1 && *s1 == *s2)
  77.     s1++, s2++;
  78.   return *s1 - *s2;
  79. }
  80.  
  81.  
  82. /* Set a given number of bytes to zero given a starting pointer. */
  83.  
  84. void ClearB(pb, cb)
  85. lpbyte pb;
  86. int cb;
  87. {
  88.   while (cb-- > 0)
  89.     *pb++ = 0;
  90. }
  91.  
  92.  
  93. /* Copy a given number of bytes from one location to another. */
  94.  
  95. void CopyRgb(pbSrc, pbDst, cb)
  96. byte *pbSrc, *pbDst;
  97. int cb;
  98. {
  99.   while (cb-- > 0)
  100.     *pbDst++ = *pbSrc++;
  101. }
  102.  
  103.  
  104. /* Determine the sign of a number: -1 if value negative, +1 if value */
  105. /* positive, and 0 if it's zero.                                     */
  106.  
  107. real RSgn(r)
  108. real r;
  109. {
  110.   return r == 0.0 ? 0.0 : RSgn2(r);
  111. }
  112.  
  113.  
  114. /* Given an x and y coordinate, return the angle formed by a line from the */
  115. /* origin to this coordinate. This is just converting from rectangular to  */
  116. /* polar coordinates; however, we don't determine the radius here.         */
  117.  
  118. real Angle(x, y)
  119. real x, y;
  120. {
  121.   real a;
  122.  
  123.   if (x != 0.0) {
  124.     if (y != 0.0)
  125.       a = RAtn(y/x);
  126.     else
  127.       a = x < 0.0 ? rPi : 0.0;
  128.   } else
  129.     a = y < 0.0 ? -rPiHalf : rPiHalf;
  130.   if (a < 0.0)
  131.     a += rPi;
  132.   if (y < 0.0)
  133.     a += rPi;
  134.   return a;
  135. }
  136.  
  137.  
  138. /* Modulus function for floating point values, where we bring the given */
  139. /* parameter to within the range of 0 to 360.                           */
  140.  
  141. real Mod(d)
  142. real d;
  143. {
  144.   if (d >= rDegMax)        /* In most cases, our value is only slightly */
  145.     d -= rDegMax;          /* out of range, so we can test for it and   */
  146.   else if (d < 0.0)        /* avoid the more complicated arithmetic.    */
  147.     d += rDegMax;
  148.   if (d >= 0 && d < rDegMax)
  149.     return d;
  150.   return (d - RFloor(d/rDegMax)*rDegMax);
  151. }
  152.  
  153.  
  154. /* Another modulus function, this time for the range of 0 to 2 Pi. */
  155.  
  156. real ModRad(r)
  157. real r;
  158. {
  159.   while (r >= rPi2)    /* We assume our value is only slightly out of       */
  160.     r -= rPi2;         /* range, so test and never do any complicated math. */
  161.   while (r < 0.0)
  162.     r += rPi2;
  163.   return r;
  164. }
  165.  
  166.  
  167. /* Integer division - like the "/" operator but always rounds result down. */
  168.  
  169. long Dvd(x, y)
  170. long x, y;
  171. {
  172.   long z;
  173.  
  174.   if (y == 0)
  175.     return x;
  176.   z = x / y;
  177.   if (((x >= 0) == (y >= 0)) || x-z*y == 0)
  178.     return z;
  179.   return z - 1;
  180. }
  181.  
  182.  
  183. /*
  184. ******************************************************************************
  185. ** General Astrology Procedures.
  186. ******************************************************************************
  187. */
  188.  
  189. /* A similar modulus function: convert an integer to value from 1..12. */
  190.  
  191. int Mod12(i)
  192. int i;
  193. {
  194.   while (i > cSign)
  195.     i -= cSign;
  196.   while (i < 1)
  197.     i += cSign;
  198.   return i;
  199. }
  200.  
  201.  
  202. /* Convert an inputed fractional degrees/minutes value to a true decimal   */
  203. /* degree quantity. For example, the user enters the decimal value "10.30" */
  204. /* to mean 10 degrees and 30 minutes; this will return 10.5, i.e. 10       */
  205. /* degrees and 30 minutes expressed as a floating point degree value.      */
  206.  
  207. real DecToDeg(d)
  208. real d;
  209. {
  210.   return RSgn(d)*(RFloor(RAbs(d))+RFract(RAbs(d))*100.0/60.0);
  211. }
  212.  
  213.  
  214. /* This is the inverse of the above function. Given a true decimal value */
  215. /* for a zodiac degree, adjust it so the degrees are in the integer part */
  216. /* and the minute expressed as hundredths, e.g. 10.5 degrees -> 10.30    */
  217.  
  218. real DegToDec(d)
  219. real d;
  220. {
  221.   return RSgn(d)*(RFloor(RAbs(d))+RFract(RAbs(d))*60.0/100.0);
  222. }
  223.  
  224.  
  225. /* Return the shortest distance between two degrees in the zodiac. This is  */
  226. /* normally their difference, but we have to check if near the Aries point. */
  227.  
  228. real MinDistance(deg1, deg2)
  229. real deg1, deg2;
  230. {
  231.   real i;
  232.  
  233.   i = RAbs(deg1-deg2);
  234.   return i < rDegHalf ? i : rDegMax - i;
  235. }
  236.  
  237.  
  238. /* This is just like the above routine, except the min distance value  */
  239. /* returned will either be positive or negative based on whether the   */
  240. /* second value is ahead or behind the first one in a circular zodiac. */
  241.  
  242. real MinDifference(deg1, deg2)
  243. real deg1, deg2;
  244. {
  245.   real i;
  246.  
  247.   i = deg2 - deg1;
  248.   if (RAbs(i) < rDegHalf)
  249.     return i;
  250.   return RSgn(i)*(RAbs(i) - rDegMax);
  251. }
  252.  
  253.  
  254. /* Return the degree of the midpoint between two zodiac positions, making */
  255. /* sure we return the true midpoint closest to the positions in question. */
  256.  
  257. real Midpoint(deg1, deg2)
  258. real deg1, deg2;
  259. {
  260.   real mid;
  261.  
  262.   mid = (deg1+deg2)/2.0;
  263.   return MinDistance(deg1, mid) < rDegQuad ? mid : Mod(mid+rDegHalf);
  264. }
  265.  
  266.  
  267. /* Given a planet and sign, determine whether: The planet rules the sign, */
  268. /* the planet has its fall in the sign, the planet exalts in the sign, or */
  269. /* is debilitated in the sign; and return an appropriate character.       */
  270.  
  271. char Dignify(obj, sign)
  272. int obj, sign;
  273. {
  274.   if (obj > oNorm)
  275.     return ' ';
  276.   if (ruler1[obj] == sign || ruler2[obj] == sign)
  277.     return 'R';
  278.   if (ruler1[obj] == Mod12(sign+6) || ruler2[obj] == Mod12(sign+6))
  279.     return 'F';
  280.   if (exalt[obj] == sign)
  281.     return 'e';
  282.   if (exalt[obj] == Mod12(sign+6))
  283.     return 'd';
  284.   return '-';
  285. }
  286.  
  287.  
  288. /* Determine the number of days in a particular month. The year is needed, */
  289. /* too, because we have to check for leap years in the case of February.   */
  290.  
  291. int DayInMonth(month, year)
  292. int month, year;
  293. {
  294.   int d;
  295.  
  296.   if (month == mSep || month == mApr || month == mJun || month == mNov)
  297.     d = 30;
  298.   else if (month != mFeb)
  299.     d = 31;
  300.   else {
  301.     d = 28;
  302.     if (year % 4 == 0 &&
  303.       (year % 100 != 0 || year % 400 == 0 || year <= yeaJ2G))
  304.       d++;
  305.   }
  306.   return d;
  307. }
  308.  
  309.  
  310. /* Return the actual number of days in a particular month. Normally, this  */
  311. /* is the same as the above routine which determines the index of the last */
  312. /* day of the month, but the values can differ when changing between       */
  313. /* calendar systems (Julian to Gregorian) in which one can jump over days. */
  314.  
  315. int DaysInMonth(month, year)
  316. int month, year;
  317. {
  318.   int d;
  319.  
  320.   d = DayInMonth(month, year);
  321.   if (year == yeaJ2G && month == monJ2G)
  322.     d -= (dayJ2G2 - dayJ2G1 - 1);
  323.   return d;
  324. }
  325.  
  326.  
  327. /* Return the day of the week (Sunday is 0) of the specified given date. */
  328.  
  329. int DayOfWeek(month, day, year)
  330. int month, day, year;
  331. {
  332.   int d;
  333.  
  334.   d = (int)((MdyToJulian(month, day, year) + 1) % 7);
  335.   return d < 0 ? d+7 : d;
  336. }
  337.  
  338.  
  339. /* Given a day, and the month and year it falls in, add a number of days    */
  340. /* to it and return the new day index. As month changes are not checked for */
  341. /* here, this is mostly just adding the offset to the day; however we need  */
  342. /* to check for calendar changes for when days in a month may be skipped.   */
  343.  
  344. int AddDay(month, day, year, delta)
  345. int month, day, year, delta;
  346. {
  347.   int d;
  348.  
  349.   d = day + delta;
  350.   if (year == yeaJ2G && month == monJ2G) {     /* Check for Julian to  */
  351.     if (d > dayJ2G1 && d < dayJ2G2)            /* Gregorian crossover. */
  352.       d += NSgn(delta)*(dayJ2G2-dayJ2G1-1);
  353.   }
  354.   return d;
  355. }
  356.  
  357.  
  358. /* Given an aspect and two objects making that aspect with each other,   */
  359. /* return the maximum orb allowed for such an aspect. Normally this only */
  360. /* depends on the aspect itself, but some objects require narrow orbs,   */
  361. /* and some allow wider orbs, so check for these cases.                  */
  362.  
  363. real GetOrb(obj1, obj2, asp)
  364. int obj1, obj2, asp;
  365. {
  366.   real orb, i;
  367.  
  368.   orb = rAspOrb[asp];
  369.   i = obj1 > oNorm ? 2.0 : rObjOrb[obj1];
  370.   orb = Min(orb, i);
  371.   i = obj2 > oNorm ? 2.0 : rObjOrb[obj2];
  372.   orb = Min(orb, i);
  373.   if (obj1 <= oNorm)
  374.     orb += rObjAdd[obj1];
  375.   if (obj2 <= oNorm)
  376.     orb += rObjAdd[obj2];
  377.   return orb;
  378. }
  379.  
  380.  
  381. /*
  382. ******************************************************************************
  383. ** String Procedures.
  384. ******************************************************************************
  385. */
  386.  
  387. /* Exit the program, and do any cleanup necessary. Note that if we had     */
  388. /* a non-fatal error, and we are in the -Q loop mode, then we won't        */
  389. /* actually terminate the program, but drop back to the command line loop. */
  390.  
  391. void Terminate(tc)
  392. int tc;
  393. {
  394.   char sz[cchSzDef];
  395.  
  396.   if (us.fNoQuit)
  397.     return;
  398.   if (tc == tcForce) {
  399.     S = stdout;
  400.     AnsiColor(kWhite);
  401.     sprintf(sz, "\n%s %s exited.\n", szAppName, szVersionCore);
  402.     PrintSz(sz);
  403.   }
  404.   if (tc == tcError && us.fLoop)
  405.     return;
  406.   if (us.fAnsi) {
  407.     sprintf(sz, "%c[0m", chEscape);    /* Get out of any Ansi color mode. */
  408.     PrintSz(sz);
  409.   }
  410.   exit(abs(tc));
  411. }
  412.  
  413.  
  414. /* Print a string on the screen. A seemingly simple operation, however we */
  415. /* keep track of what column we are printing at after each newline so we  */
  416. /* can automatically clip at the appropriate point, and we keep track of  */
  417. /* the row we are printing at, so we may prompt before screen scrolling.  */
  418.  
  419. void PrintSz(sz)
  420. CONST char *sz;
  421. {
  422.   char szInput[cchSzDef], *pch;
  423. #ifndef WIN
  424.   int nT;
  425. #endif
  426.  
  427.   for (pch = (char *)sz; *pch; pch++) {
  428.     if (*pch != '\n') {
  429.       is.cchCol++;
  430.       if (us.fClip80 && is.cchCol >= us.nScreenWidth)  /* Clip if need be. */
  431.         continue;
  432.     } else {
  433.       is.cchRow++;
  434.       is.cchCol = 0;
  435.     }
  436. #ifdef WIN
  437.     if (S == stdout) {
  438.       if ((byte)*pch >= ' ') {
  439.         szInput[0] = *pch; szInput[1] = chNull;
  440.         TextOut(wi.hdc, (is.cchCol - 1 - wi.xScroll * 10) * wi.xChar + 4,
  441.           (is.cchRow - wi.yScroll * 10) * wi.yChar, szInput, 1);
  442.       }
  443.     } else
  444. #endif
  445.     putc(*pch, S);
  446. #ifndef WIN
  447.     if (*pch == '\n' && S == stdout &&
  448.       us.nScrollRow > 0 && is.cchRow >= us.nScrollRow) {
  449.  
  450.       /* If we've printed 'n' rows, stop and wait for a line to be entered. */
  451.  
  452.       nT = us.fAnsi;
  453.       us.fAnsi = 0;
  454.       InputString("Press return to continue scrolling", szInput);
  455.       us.fAnsi = nT;
  456.       is.cchRow = 0;
  457.  
  458.       /* One can actually give a few simple commands before hitting return. */
  459.  
  460.       if (szInput[0] == '.' || szInput[0] == 'q')
  461.         Terminate(tcForce);
  462.       else if (szInput[0] == '8')
  463.         not(us.fClip80);
  464.       else if (szInput[0] == 'Q')
  465.         us.nScrollRow = 0;
  466.       else if (szInput[0] == 'k') {
  467.         if (us.fAnsi)
  468.           AnsiColor(kDefault);
  469.         not(us.fAnsi);
  470.       }
  471.     }
  472. #else
  473.     if (*pch == '\n' && S == stdout && wi.hdcPrint != hdcNil &&
  474.       is.cchRow >= us.nScrollRow) {
  475.  
  476.       /* If writing to the printer, start a new page when appropriate. */
  477.  
  478.       is.cchRow = 0;
  479.       EndPage(wi.hdcPrint);
  480.       StartPage(wi.hdcPrint);
  481.       /* StartPage clobbers all the DC settings */
  482.       SetMapMode(wi.hdcPrint, MM_ANISOTROPIC);      /* For SetViewPortExt */
  483.       SetViewportOrg(wi.hdcPrint, 0, 0);
  484.       SetViewportExt(wi.hdcPrint, GetDeviceCaps(wi.hdcPrint, HORZRES),
  485.         GetDeviceCaps(wi.hdcPrint, VERTRES));
  486.       SetWindowOrg(wi.hdcPrint, 0, 0);
  487.       SetWindowExt(wi.hdcPrint, wi.xClient, wi.yClient);
  488.       SetBkMode(wi.hdcPrint, TRANSPARENT);
  489.       SelectObject(wi.hdcPrint, wi.hfont);
  490.     }
  491. #endif
  492.   }
  493. }
  494.  
  495.  
  496. /* Print a single character on the screen. */
  497.  
  498. void PrintCh(ch)
  499. char ch;
  500. {
  501.   char sz[2];
  502.  
  503.   sz[0] = ch; sz[1] = chNull;    /* Treat char as a string of length one. */
  504.   PrintSz(sz);                   /* Then call above to print the string.  */
  505. }
  506.  
  507.  
  508. /* Print a string on the screen. Unlike the normal PrintSz(), here we still */
  509. /* go to the standard output even if text is being sent to a file with -os. */
  510.  
  511. void PrintSzScreen(sz)
  512. char *sz;
  513. {
  514.   FILE *fileT;
  515.  
  516.   fileT = S;
  517.   S = stdout;
  518.   PrintSz(sz);
  519.   S = fileT;
  520. }
  521.  
  522.  
  523. /* Print a general user message given a string. This is just like the */
  524. /* warning displayer below just that we print in a different color.   */
  525.  
  526. void PrintNotice(sz)
  527. char *sz;
  528. {
  529. #ifndef WIN
  530.   /* Notice messages are ignored in the Windows version. */
  531.   AnsiColor(kYellow);
  532.   fprintf(stderr, "%s\n", sz);
  533.   AnsiColor(kDefault);
  534. #endif
  535. }
  536.  
  537.  
  538. /* Print a warning message given a string. This is called in non-fatal  */
  539. /* cases where we return to normal execution after printing the string. */
  540.  
  541. void PrintWarning(sz)
  542. char *sz;
  543. {
  544. #ifndef WIN
  545.   AnsiColor(kRed);
  546.   fprintf(stderr, "%s\n", sz);
  547.   AnsiColor(kDefault);
  548. #else
  549.   char szT[cchSzDef];
  550.  
  551.   sprintf(szT, "%s Warning", szAppName);
  552.   MessageBox((HWND)NULL, sz, szT, MB_ICONSTOP);
  553. #endif
  554. }
  555.  
  556.  
  557. /* Print an error message. This is called in more serious cases which halt */
  558. /* running of the current chart sequence, which can terminate the program  */
  559. /* but isn't a fatal error in that we can still fall back to the -Q loop.  */
  560.  
  561. void PrintError(sz)
  562. char *sz;
  563. {
  564. #ifndef WIN
  565.   AnsiColor(kRed);
  566.   fprintf(stderr, "%s: %s\n", szAppName, sz);
  567.   Terminate(tcError);
  568.   AnsiColor(kDefault);
  569. #else
  570.   char szT[cchSzDef];
  571.  
  572.   sprintf(szT, "%s Error", szAppName);
  573.   MessageBox((HWND)NULL, sz, szT, MB_ICONEXCLAMATION);
  574. #endif
  575. }
  576.  
  577.  
  578. /* Simplification for a commonly printed error message. */
  579.  
  580. void ErrorArgc(szOpt)
  581. char *szOpt;
  582. {
  583.   char sz[cchSzDef];
  584.  
  585.   sprintf(sz, "Too few options to switch %c%s", chSwitch, szOpt);
  586.   PrintError(sz);
  587. }
  588.  
  589.  
  590. /* Another simplification for a commonly printed error message. */
  591.  
  592. void ErrorValN(szOpt, nVal)
  593. char *szOpt;
  594. int nVal;
  595. {
  596.   char sz[cchSzDef];
  597.  
  598.   sprintf(sz, "Value %d passed to switch %c%s out of range.\n",
  599.     nVal, chSwitch, szOpt);
  600.   PrintError(sz);
  601. }
  602.  
  603.  
  604. /* Yet another place to print a type of error message. */
  605.  
  606. void ErrorArgv(szOpt)
  607. char *szOpt;
  608. {
  609.   char sz[cchSzDef];
  610.  
  611.   sprintf(sz, "The switch %c%s is not allowed now.\n", chSwitch, szOpt);
  612.   PrintError(sz);
  613. }
  614.  
  615.  
  616. /* Still another place to print a type of error message. */
  617.  
  618. void ErrorSwitch(szOpt)
  619. char *szOpt;
  620. {
  621.   char sz[cchSzDef];
  622.  
  623.   sprintf(sz, "Unknown switch '%s'", szOpt);
  624.   PrintError(sz);
  625. }
  626.  
  627.  
  628. #ifdef PLACALC
  629. /* Print error messages dealing with ephemeris file access. */
  630.  
  631. void ErrorEphem(sz, l)
  632. char *sz;
  633. long l;
  634. {
  635.   char szT[cchSzDef];
  636.  
  637.   if (l < 0)
  638.     sprintf(szT, "Ephemeris file %s not found.\n", sz);
  639.   else
  640.     sprintf(szT, "Seek error in file %s at position %ld.\n", sz, l);
  641.   PrintWarning(szT);
  642. }
  643. #endif
  644.  
  645.  
  646. /* A simple procedure used throughout Astrolog: Print a particular */
  647. /* character on the screen 'n' times.                              */
  648.  
  649. void PrintTab(ch, cch)
  650. char ch;
  651. int cch;
  652. {
  653.   int i;
  654.  
  655.   for (i = 0; i < cch; i++)
  656.     PrintCh(ch);
  657. }
  658.  
  659.  
  660. /* Set an Ansi or MS Windows text color. */
  661.  
  662. void AnsiColor(k)
  663. int k;
  664. {
  665.   char sz[cchSzDef];
  666.   int cchSav;
  667.  
  668. #ifdef WIN
  669.   if (S == stdout) {
  670.     if (k < 0)
  671.       k = kLtGray;
  672.     SetTextColor(wi.hdc,
  673.       (COLORREF)rgbbmp[us.fAnsi ? k : (gs.fInverse ? kBlack : kLtGray)]);
  674.     return;
  675.   }
  676. #endif
  677.  
  678.   /* Special case: If we are passed the value Reverse, and ansi is not    */
  679.   /* only on but set to a value > 1, then we'll enter reverse video mode. */
  680.  
  681.   if (!us.fAnsi || (k == kReverse && us.fAnsi < 2))
  682.     return;
  683.   cchSav = is.cchCol;
  684.   is.cchCol = 0;
  685.   sprintf(sz, "%c[", chEscape);
  686.   PrintSz(sz);
  687.   if (k == kDefault)
  688.     PrintCh('0');
  689.   else if (k == kReverse) {
  690.     PrintCh('7');
  691.   } else {
  692.     sprintf(sz, "%c;%d", k > 7 ? '1' : '0', 30 + (k & 7));
  693.     PrintSz(sz);
  694.   }
  695.   PrintCh('m');
  696.   is.cchCol = cchSav;
  697. }
  698.  
  699.  
  700. /* Print a zodiac position on the screen. This basically just prints the */
  701. /* string returned from SzZodiac() below, except we take care of color.  */
  702.  
  703. void PrintZodiac(deg)
  704. real deg;
  705. {
  706.   AnsiColor(kElemA[(int)(deg / 30.0) & 3]);
  707.   PrintSz(SzZodiac(deg));
  708.   AnsiColor(kDefault);
  709. }
  710.  
  711.  
  712. /* Given a zodiac position, return a string containing it as it's */
  713. /* formatted for display to the user.                             */
  714.  
  715. char *SzZodiac(deg)
  716. real deg;
  717. {
  718.   static char zod[11];
  719.   int sign, d, m;
  720.   real s;
  721.  
  722.   switch (us.nDegForm) {
  723.   case 0:
  724.  
  725.     /* Normally, we format the position in degrees/sign/minutes format: */
  726.  
  727.     deg = Mod(deg + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0));
  728.     sign = (int)(deg / 30.0);
  729.     d = (int)deg - sign*30;
  730.     m = (int)(RFract(deg)*60.0);
  731.     sprintf(zod, "%2d%c%c%c%02d", d, chSig3(sign + 1), m);
  732.     if (is.fSeconds) {
  733.       s = RFract(deg)*60.0; s = RFract(s)*60.0;
  734.       sprintf(&zod[7], "'%02d\"", (int)s);
  735.     }
  736.     break;
  737.  
  738.   case 1:
  739.     /* However, if -sh switch in effect, get position in hours/minutes: */
  740.  
  741.     deg = Mod(deg + (is.fSeconds ? rRound/4.0/60.0 : rRound/4.0));
  742.     d = (int)(deg / 15.0);
  743.     m = (int)((deg - (real)d*15.0)*4.0);
  744.     sprintf(zod, "%2dh,%02dm", d, m);
  745.     if (is.fSeconds) {
  746.       s = RFract(deg)*4.0; s = RFract(s)*60.0;
  747.       sprintf(&zod[7], ",%02ds", (int)s);
  748.     }
  749.     break;
  750.  
  751.   default:
  752.     /* Otherwise, if -sd in effect, format position as a simple degree: */
  753.  
  754.     sprintf(zod, is.fSeconds ? "%11.7f" : "%7.3f", deg);
  755.     break;
  756.   }
  757.   return zod;
  758. }
  759.  
  760.  
  761. /* This is similar to formatting a zodiac degree, but here we return a */
  762. /* string of a (signed) declination value in degrees and minutes.      */
  763.  
  764. char *SzAltitude(deg)
  765. real deg;
  766. {
  767.   static char alt[10];
  768.   int d, m, f;
  769.   real s;
  770.   char ch;
  771.  
  772.   f = deg < 0.0;
  773.   deg = RAbs(deg) + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0);
  774.   d = (int)deg;
  775.   m = (int)(RFract(deg)*60.0);
  776.   ch = us.fAnsi == -1 ? 128 : chDeg1;
  777.   sprintf(alt, "%c%2d%c%02d'", f ? '-' : '+', d, ch, m);
  778.   if (is.fSeconds) {
  779.     s = RFract(deg)*60.0; s = RFract(s)*60.0;
  780.     sprintf(&alt[7], "%02d\"", (int)s);
  781.   }
  782.   return alt;
  783. }
  784.  
  785.  
  786. /* Here we return a string simply expressing the given value as degrees */
  787. /* and minutes (and sometimes seconds) in the 0 to 360 degree circle.   */
  788.  
  789. char *SzDegree(deg)
  790. real deg;
  791. {
  792.   static char pos[11];
  793.   int d, m;
  794.   real s;
  795.  
  796.   deg = RAbs(deg) + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0);
  797.   d = (int)deg;
  798.   m = (int)(RFract(deg)*60.0);
  799.   sprintf(pos, "%3d%c%02d'", d, chDeg1, m);
  800.   if (is.fSeconds) {
  801.     s = RFract(deg)*60.0; s = RFract(s)*60.0;
  802.     sprintf(&pos[7], "%02d\"", (int)s);
  803.   }
  804.   return pos;
  805. }
  806.  
  807.  
  808. /* Another string formatter, here we return a date string given a month,    */
  809. /* day, and year. We format with the day or month first based on whether    */
  810. /* the "European" date variable is set or not. The routine also takes a     */
  811. /* parameter to indicate how much the string should be abbreviated, if any. */
  812.  
  813. char *SzDate(mon, day, yea, nFormat)
  814. int mon, day, yea, nFormat;
  815. {
  816.   static char szDate[20];
  817.  
  818.   if (us.fEuroDate) {
  819.     switch (nFormat) {
  820.     case  2: sprintf(szDate, "%2d %c%c%c%5d", day, chMon3(mon), yea); break;
  821.     case  1: sprintf(szDate, "%d %s %d", day, szMonth[mon], yea);     break;
  822.     case -1: sprintf(szDate, "%2d-%2d-%2d", day, mon, abs(yea)%100);  break;
  823.     default: sprintf(szDate, "%2d-%2d-%4d", day, mon, yea);           break;
  824.     }
  825.   } else {
  826.     switch (nFormat) {
  827.     case  3: sprintf(szDate, "%c%c%c %2d, %d", chMon3(mon), day, yea); break;
  828.     case  2: sprintf(szDate, "%c%c%c %2d%5d", chMon3(mon), day, yea);  break;
  829.     case  1: sprintf(szDate, "%s %d, %d", szMonth[mon], day, yea);     break;
  830.     case -1: sprintf(szDate, "%2d/%2d/%2d", mon, day, abs(yea)%100);   break;
  831.     default: sprintf(szDate, "%2d/%2d/%4d", mon, day, yea);            break;
  832.     }
  833.   }
  834.   return szDate;
  835. }
  836.  
  837.  
  838. /* Return a string containing the given time expressed as an hour and */
  839. /* minute quantity. This is formatted in 24 hour or am/pm time based  */
  840. /* on whether the "European" time format flag is set or not.          */
  841.  
  842. char *SzTime(hr, min)
  843. int hr, min;
  844. {
  845.   static char tim[8];
  846.  
  847.   if (us.fEuroTime)
  848.     sprintf(tim, "%2d:%02d", hr, min);
  849.   else
  850.     sprintf(tim, "%2d:%02d%cm", Mod12(hr), min, hr < 12 ? 'a' : 'p');
  851.   return tim;
  852. }
  853.  
  854.  
  855. /* This just determines the correct hour and minute and calls the above. */
  856.  
  857. char *SzTim(tim)
  858. real tim;
  859. {
  860.   return SzTime(NFloor(tim), (int)(RFract(RAbs(tim))*100.0+rRound/60.0));
  861. }
  862.  
  863.  
  864. /* Return a string containing the given time zone, given as a real value     */
  865. /* having the hours before GMT in the integer part and minutes fractionally. */
  866.  
  867. char *SzZone(zon)
  868. real zon;
  869. {
  870.   static char tim[7];
  871.  
  872.   sprintf(tim, "%c%d:%02d", zon > 0.0 ? '-' : '+', (int)RAbs(zon),
  873.     (int)(RFract(RAbs(zon))*100.0+rRound/60.0));
  874.   return tim;
  875. }
  876.  
  877.  
  878. /* Nicely format the given longitude and latitude locations and return    */
  879. /* them in a string. Various parts of the program display a chart header, */
  880. /* and this allows the similar computations to be coded only once.        */
  881.  
  882. char *SzLocation(lon, lat)
  883. real lon, lat;
  884. {
  885.   static char loc[15];
  886.   int i, j;
  887.   char ch;
  888.  
  889.   i = (int)(RFract(RAbs(lon))*100.0+rRound);
  890.   j = (int)(RFract(RAbs(lat))*100.0+rRound);
  891.   ch = us.fAnsi < 0 ? 128 : chDeg1;
  892.   if (us.fAnsi >= -1) {
  893.     sprintf(loc, "%3.0f%c%02d%c%3.0f%c%02d%c",
  894.       RFloor(RAbs(lon)), ch, i, lon < 0.0 ? 'E' : 'W',
  895.       RFloor(RAbs(lat)), ch, j, lat < 0.0 ? 'S' : 'N');
  896.   } else {
  897.     sprintf(loc, "%3.0f%c%02d%3.0f%c%02d",
  898.       RFloor(RAbs(lon)), lon < 0.0 ? 'E' : 'W', i,
  899.       RFloor(RAbs(lat)), lat < 0.0 ? 'S' : 'N', j);
  900.   }
  901.   return loc;
  902. }
  903.  
  904.  
  905. #ifdef TIME
  906. /* Compute the date and time it is right now as the program is running      */
  907. /* using the computer's internal clock. We do this by getting the number    */
  908. /* of seconds which have passed since January 1, 1970 and going from there. */
  909. /* The time return value filled is expressed in the given zone parameter.   */
  910.  
  911. void GetTimeNow(mon, day, yea, tim, zon)
  912. int *mon, *day, *yea;
  913. real *tim, zon;
  914. {
  915.   dword curtimer;
  916.   int min, sec;
  917.   real hr;
  918.  
  919.   time(&curtimer);
  920.   sec = (int)(curtimer % 60);
  921.   curtimer = curtimer / 60 + us.lTimeAddition;
  922.   min = (int)(curtimer % 60);
  923.   curtimer /= 60;
  924. #ifdef MAC
  925.   curtimer += 8;
  926. #endif
  927.   hr = (real)(curtimer % 24) - zon;
  928.   curtimer /= 24;
  929.   while (hr < 0.0) {
  930.     curtimer--;
  931.     hr += 24.0;
  932.   }
  933.   while (hr >= 24.0) {
  934.     curtimer++;
  935.     hr -= 24.0;
  936.   }
  937.   curtimer += ldTime;  /* Number of days between 1/1/1970 and 1/1/4713 BC. */
  938.   JulianToMdy((real)curtimer, mon, day, yea);
  939.   *tim = hr + (real)min / 100.0 + (real)sec / 6000.0;
  940. }
  941. #endif /* TIME */
  942.  
  943.  
  944. #ifdef PCG
  945. /* Map one character value to another. This is used in processing special  */
  946. /* keys and alt key combinations, which are read in from the keyboard as a */
  947. /* zero immediately followed by some value. This converts that value into  */
  948. /* something more useful to process and deal with.                         */
  949.  
  950. int NFromAltN(nAlt)
  951. int nAlt;
  952. {
  953.   /* Map number pad keys to the numbers characters they correspond to. */
  954.   if (nAlt == 82)
  955.     return '0';
  956.   else if (FBetween(nAlt, 79, 81))
  957.     return '1' + nAlt - 79;
  958.   else if (FBetween(nAlt, 75, 77))
  959.     return '4' + nAlt - 75;
  960.   else if (FBetween(nAlt, 71, 73))
  961.     return '7' + nAlt - 71;
  962.  
  963.   /* Map F1 through F12 function keys to the values 201-212. */
  964.   else if (FBetween(nAlt, 59, 68))
  965.     return 201 + nAlt - 59;
  966.   else if (FBetween(nAlt, 133, 134))
  967.     return 211 + nAlt - 133;
  968.  
  969.   /* Map Shift+F1 through Shift+F12 keys to the values 213-224. */
  970.   else if (FBetween(nAlt, 84, 93))
  971.     return 213 + nAlt - 84;
  972.   else if (FBetween(nAlt, 135, 136))
  973.     return 223 + nAlt - 135;
  974.  
  975.   /* Map Control+F1 through Control+F12 keys to the values 225-236. */
  976.   else if (FBetween(nAlt, 94, 103))
  977.     return 225 + nAlt - 94;
  978.   else if (FBetween(nAlt, 137, 138))
  979.     return 235 + nAlt - 137;
  980.  
  981.   /* Map Alt+F1 through Alt+F12 keys to the values 237-248. */
  982.   else if (FBetween(nAlt, 104, 113))
  983.     return 237 + nAlt - 104;
  984.   else if (FBetween(nAlt, 139, 140))
  985.     return 247 + nAlt - 139;
  986.  
  987.   return chNull;
  988. }
  989. #endif
  990.  
  991.  
  992. /* Given a string representing the complete pathname to a file, strip off    */
  993. /* all the path information leaving just the filename itself. This is called */
  994. /* by the main program to determine the name of the Astrolog executable.     */
  995.  
  996. char *ProcessProgname(szPath)
  997. char *szPath;
  998. {
  999.   char *b, *c, *e;
  1000.  
  1001.   b = c = szPath;
  1002.   while (*c) {
  1003. #ifdef PC
  1004.     *c = ChUncap(*c);    /* Because DOS filenames are case insensitive. */
  1005. #endif
  1006.     c++;
  1007.   }
  1008.   e = c;
  1009.   while (c > b && *c != '.')
  1010.     c--;
  1011.   if (c > b)
  1012.     *c = 0;
  1013.   else
  1014.     c = e;
  1015.   while (c > b && *c != chDirSep)
  1016.     c--;
  1017.   if (c > b)
  1018.     szPath = c+1;
  1019.   return szPath;
  1020. }
  1021.  
  1022.  
  1023. /* Given a string, return a pointer to a persistent version of it, where  */
  1024. /* 'persistent' means its contents won't be invalidated when the stack    */
  1025. /* frame changes. Strings such as macros, et al, need to be in their own  */
  1026. /* space and can't just be local variables in a function reading them in. */
  1027.  
  1028. char *SzPersist(szSrc)
  1029. char *szSrc;
  1030. {
  1031.   char szT[cchSzDef], *szNew;
  1032.   int cb;
  1033.  
  1034.   /* Some strings such as outer level command line parameter arguments */
  1035.   /* already persist, so we can just return the same string passed in. */
  1036.   if (is.fSzPersist)
  1037.     return szSrc;
  1038.  
  1039.   /* Otherwise we make a copy of the string in the local heap and use it. */
  1040.   cb = CchSz(szSrc)+1;
  1041.   AllocateNear(szNew, cb);
  1042.   if (szNew == NULL) {
  1043.     sprintf(szT, "%s: Not enough near memory for string (%d bytes).",
  1044.       szAppName, cb);
  1045.     PrintWarning(szT);
  1046.   } else
  1047.     CopyRgb((byte *)szSrc, (byte *)szNew, cb);
  1048.   return szNew;
  1049. }
  1050.  
  1051.  
  1052. /* This is Astrolog's memory allocation routine, returning a pointer given */
  1053. /* a size, a flag for if it is a more than 64K huge allocation, and a      */
  1054. /* string to use when printing an error if the allocation fails.           */
  1055.  
  1056. lpbyte PAllocate(lcb, fHuge, szType)
  1057. long lcb;
  1058. bool fHuge;
  1059. char *szType;
  1060. {
  1061.   char szT[cchSzDef];
  1062.   lpbyte lp;
  1063.  
  1064.   if (fHuge)
  1065.     AllocateHuge(lp, lcb);
  1066.   else
  1067.     AllocateFar(lp, (int)lcb);
  1068. #ifdef PC
  1069.   /* For PC's the array better not cross a segment boundary. */
  1070.   if (lp && !fHuge && WHi(WLo(lp) + lcb) > 0)
  1071.     lp = NULL;
  1072. #endif
  1073.   if (lp == NULL && szType) {
  1074.     sprintf(szT, "%s: Not enough memory for %s (%ld bytes).",
  1075.       szAppName, szType, lcb);
  1076.     PrintWarning(szT);
  1077.   }
  1078.   return lp;
  1079. }
  1080.  
  1081. /* general.c */
  1082.